package com.eidos.blogs.openapi.service.seckill.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eidos.blogs.openapi.dto.secKill.GenerateRedEnvelopeDto;
import com.eidos.blogs.openapi.dto.secKill.RedEnvelopeDto;
import com.eidos.blogs.openapi.entity.seckill.RedeInfo;
import com.eidos.blogs.openapi.entity.seckill.RedeReceiveRecord;
import com.eidos.blogs.openapi.entity.seckill.RedeReceiveSyncRecords;
import com.eidos.blogs.openapi.exception.ServerIsErrorException;
import com.eidos.blogs.openapi.mapper.seckill.RedeInfoMapper;
import com.eidos.blogs.openapi.rest.HttpCodeEnum;
import com.eidos.blogs.openapi.rest.Resp;
import com.eidos.blogs.openapi.service.seckill.IRedEnvelopesService;
import com.eidos.blogs.openapi.service.seckill.IRedeInfoService;
import com.eidos.blogs.openapi.service.seckill.IRedeReceiveRecordService;
import com.eidos.blogs.openapi.service.seckill.IRedeReceiveSyncRecordsService;
import com.eidos.blogs.openapi.utils.RandomNumberUtils;
import com.eidos.blogs.openapi.vo.secKill.redenvelope.RedEnvelopeVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.FutureTask;
import java.util.stream.Collectors;

/**
 * @author ： Eidos
 * @date ：Created in 2021-01-11
 * @description：红包信息表 服务实现类
 * @modified By：
 * @version: 1.0
 */
@Service
@Slf4j
@Transactional
public class RedEnvelopesServiceImpl extends ServiceImpl<RedeInfoMapper, RedeInfo> implements IRedEnvelopesService {

    @Resource
    private Redisson redisson;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private IRedeInfoService redeInfoService;
    @Resource
    private IRedeReceiveRecordService redeReceiveRecordService;
    @Resource
    private IRedeReceiveSyncRecordsService redeReceiveSyncRecordsService;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    public Resp<List<RedeReceiveRecord>> queryRedeReceiveRecordListByRedEnvelopeId(GenerateRedEnvelopeDto dto) {
        return Resp.success(redeReceiveRecordService.queryRedeReceiveRecordListByRedEnvelopeId(dto, ((successful, result) -> {
            if (result != null) {
                System.out.println(result.toString());
            }
        })));
    }

    @Transactional
    @Override
    public Resp<Boolean> generateRedEnvelope(GenerateRedEnvelopeDto dto) {
        String lockKey = "redEnvelope_generateRedEnvelope_" + dto.getRedEnvelopeId();
        RLock lock = redisson.getLock(lockKey);
        if(null == lock){
            return Resp.failed("获取锁失败");
        }
        try {
            lock.lock();
            String numberKey = "redEnvelope_number_" + dto.getRedEnvelopeId();
            String amountKey = "redEnvelope_amount_" + dto.getRedEnvelopeId();
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(numberKey, dto.getNumber());
            if (!flag) {
                /**
                 * 初始化红包数据，抢红包拦截
                 */
                redisTemplate.delete(numberKey);
                /**
                 * 初始化红包金额，单位为分
                 */
                redisTemplate.delete(amountKey);
                return Resp.failed("该红包已经存在，不能重新添加");
            }
            flag = redisTemplate.opsForValue().setIfAbsent(amountKey, dto.getAmount());
            if (!flag) {
                /**
                 * 初始化红包数据，抢红包拦截
                 */
                redisTemplate.delete(numberKey);
                /**
                 * 初始化红包金额，单位为分
                 */
                redisTemplate.delete(amountKey);
                return Resp.failed("该红包已经存在，不能重新添加");
            }
            QueryWrapper<RedeInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("red_envelope_id", dto.getRedEnvelopeId());
            List<RedeInfo> redEnvelopes = redeInfoService.getBaseMapper().selectList(wrapper);
            if (redEnvelopes != null && redEnvelopes.size() > 0) {
                return Resp.failed("该红包已经存在，不能重新添加");
            }
            log.debug("初始化红包成功");
            log.debug("异步入库-插入红包信息记录表");
            RedeInfo redeInfo = new RedeInfo();
            redeInfo.setRedEnvelopeId(dto.getRedEnvelopeId());
            redeInfo.setUserId(dto.getUserId());
            redeInfo.setTotalNum(dto.getNumber());
            redeInfo.setTotalAmount(dto.getAmount());
            redeInfo.setType(dto.getType());
            redeInfo.setTitle(dto.getTitle());
            redeInfo.setOpenHit(dto.getOpenHit());
            redeInfo.setHitNumber(String.join(",", dto.getHitNums()));
            redeInfo.setStatus(1);
            redeInfo.setCreateTime(LocalDateTime.now());
            redeInfo.setExpireTime(LocalDateTime.now().plusDays(1L));
            redeInfoService.add(redeInfo);
            log.debug("异步入库-插入领取红包同步表");
            RedeReceiveSyncRecords redeReceiveSyncRecords = new RedeReceiveSyncRecords();
            redeReceiveSyncRecords.setRedEnvelopeId(dto.getRedEnvelopeId());
            redeReceiveSyncRecords.setRestAmount(dto.getAmount());
            redeReceiveSyncRecords.setRestNum(dto.getNumber());
            redeReceiveSyncRecords.setCreateTime(LocalDateTime.now());
            redeReceiveSyncRecordsService.add(redeReceiveSyncRecords);

            log.debug("红包ID为：" + dto.getRedEnvelopeId());
            log.debug("红包个数为：" + redisTemplate.opsForValue().get(numberKey));
            log.debug("总金额为" + redisTemplate.opsForValue().get(amountKey));
            return Resp.success(flag);
        } catch (Exception e) {
            log.error("产生异常，异常信息" + e);
            throw new ServerIsErrorException(e.getMessage());
        }finally {
            lock.unlock();
        }
    }

    @Transactional
    @Override
    public Resp<Boolean> deleteRedEnvelope(GenerateRedEnvelopeDto dto) {
        String lockKey = "redEnvelope_deleteRedEnvelope_" + dto.getRedEnvelopeId();
        RLock lock = redisson.getLock(lockKey);
        if(null == lock){
            return Resp.failed("获取锁失败");
        }
        try {
            lock.lock();
            String numberKey = "redEnvelope_number_" + dto.getRedEnvelopeId();
            String amountKey = "redEnvelope_amount_" + dto.getRedEnvelopeId();
            /**
             * 初始化红包数据，抢红包拦截
             */
            Boolean flag = redisTemplate.delete(numberKey);
            if (!flag) {
                return Resp.failed("红包不存在");
            }
            /**
             * 初始化红包金额，单位为分
             */
            flag = redisTemplate.delete(amountKey);
            if (!flag) {
                return Resp.failed("红包不存在");
            }
            log.debug("异步入库-删除红包成功");
            QueryWrapper<RedeInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("red_envelope_id", dto.getRedEnvelopeId());
            List<RedeInfo> redEnvelopes = redeInfoService.getBaseMapper().selectList(wrapper);
            if (redEnvelopes != null && redEnvelopes.size() > 0) {
                redeInfoService.removes(redEnvelopes.stream().map(RedeInfo::getId).collect(Collectors.toList()));
            }
            QueryWrapper<RedeReceiveSyncRecords> wrapper2 = new QueryWrapper<>();
            wrapper2.eq("red_envelope_id", dto.getRedEnvelopeId());
            List<RedeReceiveSyncRecords> syncRecords = redeReceiveSyncRecordsService.getBaseMapper().selectList(wrapper2);
            if (syncRecords != null && syncRecords.size() > 0) {
                redeReceiveSyncRecordsService.removes(syncRecords.stream().map(RedeReceiveSyncRecords::getId).collect(Collectors.toList()));
            }
            QueryWrapper<RedeReceiveRecord> wrapper3 = new QueryWrapper<>();
            wrapper3.eq("red_envelope_id", dto.getRedEnvelopeId());
            List<RedeReceiveRecord> redeReceiveRecords = redeReceiveRecordService.getBaseMapper().selectList(wrapper3);
            if (redeReceiveRecords != null && redeReceiveRecords.size() > 0) {
                redeReceiveRecordService.removes(redeReceiveRecords.stream().map(RedeReceiveRecord::getId).collect(Collectors.toList()));
            }
            return Resp.success(Boolean.TRUE);
        } catch (Exception e) {
            log.error("产生异常，异常信息" + e);
            throw new ServerIsErrorException(e.getMessage());
        }finally {
            lock.unlock();
        }
    }

    @Transactional
    @Override
    public Resp<RedEnvelopeVo> receiveRedEnvelope(RedEnvelopeDto dto) {
        try {
            FutureTask<Resp<RedEnvelopeVo>> futureTask = new FutureTask<>(() -> getRedEnvelope(dto));
//            RedEnVelopeFutureTask redEnVelopeFutureTask = new RedEnVelopeFutureTask(dto);
//            TemplateFutureTask futureTask = new TemplateFutureTask(redEnVelopeFutureTask);
            //1线程池提交
            threadPoolTaskExecutor.submit(futureTask);
            Resp<RedEnvelopeVo> resp = futureTask.get();
            log.debug(resp.toString());
            return resp;
        } catch (Exception e) {
            log.error("产生异常，异常信息" + e);
            throw new ServerIsErrorException(e.getMessage());
        }

    }

    @Transactional
    @Override
    public Resp<RedEnvelopeVo> getRedEnvelope(RedEnvelopeDto dto) {
        Long money = 0L;
        String lockKey = "redEnvelope_getRedEnvelope_" + dto.getRedEnvelopeId();
        String numberKey = "redEnvelope_number_" + dto.getRedEnvelopeId();
        String amountKey = "redEnvelope_amount_" + dto.getRedEnvelopeId();
        RLock lock = redisson.getLock(lockKey);
        if(null == lock){
            return Resp.failed("获取锁失败");
        }
        try {
            /**
             * 获取锁 保证红包数量和计算红包金额的原子性操作
             */
            lock.lock();
            if (redisTemplate.opsForValue().get(numberKey) == null || redisTemplate.opsForValue().get(amountKey) == null) {
                return Resp.failed("红包已经抢完了");
            }
            Long restNumber = Long.valueOf(redisTemplate.opsForValue().get(numberKey).toString());
            Long restMoney = Long.valueOf(redisTemplate.opsForValue().get(amountKey).toString());
            if (restNumber - 1 < 0) {
                return Resp.failed("红包已经抢完了");
            }
            log.debug("异步入redis-用户");
            String redisKey = "redEnvelope_" + dto.getRedEnvelopeId() + "_" + dto.getUserId();
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(redisKey, dto.getUserId());
            if (!flag) {
                return Resp.failed("该用户" + dto.getUserId() + "已经抢过了，不能重复抢");
            }
            LocalDateTime nowTime = LocalDateTime.now();
            log.debug("目前红包个数为：" + restNumber);
            log.debug("目前红包总金额为：" + restMoney);
            restNumber = redisTemplate.opsForValue().decrement(numberKey, 1);
            /**
             * 如果是最后一人
             */
            if (restNumber == 0) {
                money = restMoney;
            } else {
                if (dto.getReceiveAmount() != null && dto.getReceiveAmount().compareTo(0L) > 0) {
                    money = dto.getReceiveAmount();
                } else {
                    money = RandomNumberUtils.getRandomMoney(restNumber.intValue(), restMoney, dto.getOpenHit(), dto.getHitNums());
                }
            }
            Long decrementMoney = redisTemplate.opsForValue().decrement(amountKey, money);

            log.debug("异步入库-用户" + dto.getUserId());
            log.debug("抢到红包-金额（分）为：" + money);
            log.debug("异步入库-插入领取红包记录表");
            RedeReceiveRecord redeReceiveRecord = new RedeReceiveRecord();
            redeReceiveRecord.setRedEnvelopeId(dto.getRedEnvelopeId());
            redeReceiveRecord.setUserId(dto.getUserId());
            redeReceiveRecord.setAmount(money);
            redeReceiveRecord.setCreateTime(nowTime);

            log.debug("异步入库-插入同步红包信息");
            log.debug("异步入库-剩余红包金额：" + decrementMoney);
            log.debug("异步入库-剩余红包个数为：" + restNumber.intValue());
            QueryWrapper<RedeReceiveSyncRecords> wrapper2 = new QueryWrapper<>();
            wrapper2.eq("red_envelope_id", dto.getRedEnvelopeId());
            List<RedeReceiveSyncRecords> syncRecords = redeReceiveSyncRecordsService.getBaseMapper().selectList(wrapper2);
            if (syncRecords != null && syncRecords.size() > 0) {
                RedeReceiveSyncRecords redeReceiveSyncRecords = syncRecords.get(0);
                redeReceiveSyncRecords.setRestNum(restNumber.intValue());
                redeReceiveSyncRecords.setRestAmount(decrementMoney);
                redeReceiveSyncRecords.setCreateTime(nowTime);

                redeReceiveRecordService.add(redeReceiveRecord);
                redeReceiveSyncRecordsService.modify(redeReceiveSyncRecords);
            } else {
                return Resp.failed(HttpCodeEnum.DATA_NULL);
            }
            // 如果为0，表示红包已经抢完
            if (restNumber == 0) {
                // 删除redis数据
                redisTemplate.delete(numberKey);
                redisTemplate.delete(amountKey);
                // 修改红包信息表状态为禁用0
                RedeInfo po = new RedeInfo();
                po.setStatus(0);
                QueryWrapper<RedeInfo> wrapper = new QueryWrapper<>();
                wrapper.eq("red_envelope_id", dto.getRedEnvelopeId());
                redeInfoService.update(po, wrapper);
                // 计算手气最佳
                QueryWrapper<RedeReceiveRecord> wrapper3 = new QueryWrapper<>();
                wrapper3.eq("red_envelope_id", dto.getRedEnvelopeId());
                List<RedeReceiveRecord> redeReceiveRecords = redeReceiveRecordService.getBaseMapper().selectList(wrapper3);
                if (!CollectionUtils.isEmpty(redeReceiveRecords)) {
                    RedeReceiveRecord redeReceiveRecord1 = redeReceiveRecords.stream().sorted((o1, o2) -> {
                        return o2.getAmount().compareTo(o1.getAmount());
                    }).collect(Collectors.toList()).get(0);
                    redeReceiveRecord1.setBest(1);
                    redeReceiveRecordService.modify(redeReceiveRecord1);
                }

            }
            // TODO 异步入账
            RedEnvelopeVo redEnvelopeVo = new RedEnvelopeVo();
            redEnvelopeVo.setRedEnvelopeId(dto.getRedEnvelopeId());
            redEnvelopeVo.setUserId(dto.getUserId());
            redEnvelopeVo.setRestNum(restNumber.intValue());
            redEnvelopeVo.setMoney(money);
            redEnvelopeVo.setCreateTime(nowTime);
            return Resp.success(redEnvelopeVo);
        } catch (Exception e) {
            log.error("产生异常，异常信息" + e);
            throw new ServerIsErrorException(e.getMessage());
        } finally {
            lock.unlock();
        }

    }

    @Transactional
    @Override
    public Resp<Long> grabConcurrent(RedEnvelopeDto dto) {
        Long money = 0L;
        String lockKey = "redEnvelope_lock_" + dto.getRedEnvelopeId();
        String numberKey = "redEnvelope_number_" + dto.getRedEnvelopeId();
        String amountKey = "redEnvelope_amount_" + dto.getRedEnvelopeId();
        RLock lock = redisson.getLock(lockKey);
        try {
            /**
             * 获取锁 保证红包数量和计算红包金额的原子性操作
             */
            lock.lock();
            Long restNumber = redisTemplate.opsForValue().decrement(numberKey, 1);
            if (restNumber >= 0) {
                /**
                 * 如果是最后一人
                 */
                if (restNumber == 0) {
                    money = Long.valueOf(redisTemplate.opsForValue().get(amountKey).toString());
                } else {
                    Long restMoney = Long.valueOf(redisTemplate.opsForValue().get(amountKey).toString());
                    log.debug("剩余总金额为：" + restMoney);
                    money = RandomNumberUtils.getRandomMoney(restNumber.intValue(), restMoney, dto.getOpenHit(), dto.getHitNums());
                }
                redisTemplate.opsForValue().decrement(amountKey, money);

                /**
                 * 异步入库
                 */
                log.debug("异步入库-剩余红包个数为：" + restNumber);
                log.debug("异步入库-用户" + dto.getUserId() + "抢到红包-金额为：" + money);
                /**
                 * 异步入账
                 */
            } else {
                return Resp.failed();
            }
            return Resp.success(money);
        } catch (Exception e) {
            log.error("产生异常，异常信息" + e);
            throw new ServerIsErrorException(e.getMessage());
        } finally {
            lock.unlock();
        }

    }


}
